#include "BreakPointService.h"
#include <cassert>
#include "Message.h"
#include "Backend.h"
#include "Util.h"
#include "BreakPointFile.h"

// service start;
//  new: 如果文件存在，则载入断点号
//    1.文件名
//    2. open && load breakpoint from file  && close
//    3. file not exist => seqno = 0;
//
// service connected:
//      send remote backend login_request(with current seqno)
//      , and then backend send login_response to this service;
// received --> 1. receive login response
//          --> 2. receive normal response 
//              new: seqNo += 1后，存入文件一次
//              open && save breakpoint to file && close file 
//
// service disconnected:
//      backend maybe got disconnected event: -> backend will clear current seqNo for this service
// service stop:
//  
BreakPointService::BreakPointService()
: BreakPointService(nullptr, "breakpoint.default.file"){
}
BreakPointService::BreakPointService(Backend * backend, const std::string & fileName)
: _seqNo(0), _backend(backend), _file(new BreakPointFile(fileName)) 
{
}
BreakPointService::~BreakPointService(){
  delete _file;
  _seqNo = 0;
  _backend = nullptr;
}


int BreakPointService::start() { 
  _seqNo = _file->load();
  return 0;
}
int BreakPointService::getBreakpoint() {
  return _seqNo ;
}

//maybe need to exception test 
int BreakPointService::sendMessage(const Message & msg){
  if(_backend != nullptr){
    return _backend->receiveLoginMessage(msg);
  }
  return -1;
}

int BreakPointService::connected(){
  Message login(_seqNo, LOGIN_REQUEST, "loginRequest"); 
  return sendMessage(login);
}
int BreakPointService::disconnected(){
  return 0;
}

int BreakPointService::receiveNormalResponse(const Message & msg){
  if(_seqNo + 1 == msg.seqNo()){
    _seqNo ++;
    return _file->save(_seqNo);
  }
  return -1;
}
int BreakPointService::receiveLoginResponse(const Message & loginMsg){
  if(loginMsg.type() != LOGIN_RESPONSE){
    return -1;
  }
  return 0;
}

int BreakPointService::stop(){
  _file->save(_seqNo);
  _seqNo = 0;
  return 0;
}



void test_breakpoint_service() {
	const char * fileName = "breakpoint.file";
	if(util::File::exists(fileName)) {
		assert(util::File::remove(fileName));
	}
  
  
  Backend backend;
  BreakPointService service(&backend, fileName);

  assert(0 == service.start());
  assert(0 == service.getBreakpoint());

  assert(0 == service.connected());
  assert(backend.receivedBreakPoint() == 0);

  {
    Message msg(1, LOGIN_RESPONSE+1, "hello1");
    assert(msg.seqNo() == 1);

    assert(0 == service.receiveNormalResponse(msg));
    assert(1 == service.getBreakpoint());
  }


  { //Case 2: check error seqno of msg --> received failed 
    Message next_recv_msg(100, LOGIN_RESPONSE + 1, "hello100");
    assert(100 == next_recv_msg.seqNo());
    assert(1 == service.getBreakpoint());
    assert(-1 == service.receiveNormalResponse(next_recv_msg));
  }

  {
    Message msg(2, LOGIN_RESPONSE+1, "hello2");
    assert(0 == service.receiveNormalResponse(msg));
    assert(2 == service.getBreakpoint());
  }

  //login test case 
  {
    Message login(3, LOGIN_RESPONSE, "hello3");
    assert(login.type() == LOGIN_RESPONSE);
    assert(0 == service.receiveLoginResponse(login));
    assert(2 == service.getBreakpoint()); // don't consume the breakpoint
  }

  {
    Message login(3, LOGIN_RESPONSE + 3, "hello3");
    assert(login.type() != LOGIN_RESPONSE);
    assert(-1 == service.receiveLoginResponse(login));
  }


  assert(0 == service.disconnected());

  assert(0 == service.stop());
  assert(0 == service.getBreakpoint());
}


// create one file  with init_value(10);
// start service 
// assert( init_value == service.getBreakpoint());
static void __fileMode_load_from_file(){
	const char * fileName = "breakpoint.load.file";
	if(util::File::exists(fileName)) {
		assert(util::File::remove(fileName));
	}

  //Case 1. file not exist  test
  Backend backend;
  {
    BreakPointService service(&backend, fileName);
    
    assert(0 == service.start());
    assert(0 == service.getBreakpoint());

    assert(0 == service.stop());
  }
  assert(util::File::exists(fileName)== true); //stop to create file

  //Case 2: write an breakpoint to file first; 
  //  then load && assert it
  int expectBreakpoint = 100;
  assert(sizeof(int) == util::File::writeAndFlushAsBinary(fileName, expectBreakpoint));  //write 100 to file

  {
    BreakPointService service(&backend, fileName);
    
    assert(0 == service.start()); //load from file to 100
    assert(expectBreakpoint == service.getBreakpoint()); 

    assert(0 == service.stop());
  }
}

// start service;
// init_value = service.getBreakpoint();
// recv one msg ==> call save_to_file();
// assert(init_value + 1 == service.getBreakpoint());
// service.stop();

// new service 
// start service
// init_value + 1 == service.getBreakpoint();
// service.stop();
static void __fileMode_save_from_file(){
	const char * fileName = "breakpoint.save.file";

  int initBreakpoint = 0;
  Backend backend;
  {
    BreakPointService service(&backend, fileName);
    
    assert(0 == service.start());
    initBreakpoint = service.getBreakpoint(); 

    {
      int next_msg_seq_no = initBreakpoint + 1;
      Message msg(next_msg_seq_no, LOGIN_RESPONSE+1, "hello1");

      assert(0 == service.receiveNormalResponse(msg));
      assert(next_msg_seq_no == service.getBreakpoint());
    }

    service.stop();
  }

  //test load from file  && save file 
  {
    BreakPointService service(&backend, fileName);
    service.start();
    assert(service.getBreakpoint() == initBreakpoint + 1);

    service.stop();
  }
}





//Design -> create BreakPointFile class
// story: 
// 1. load:
//	file not-exist => seqNo = 0
//	file exist 
//		>format-error => seqNo = 0
//		>format-correct => seqNo = real_value;
// 2. save:
//	file.save(seqNo) => write to file:
//	file-not-exist => create it  && write it 
//	file-exist 
//		> write_failed => return -1;
//		> write_ok => return 0;
static void __fileMode_refactoring(){
}

void test_breakpoint_service__fileMode(){
  __fileMode_load_from_file(); 
  __fileMode_save_from_file();

	__fileMode_refactoring();
}

